//=====================================================
//			 		          TGAImage.cpp
//		     Implementation of TGAImage class
//  		     Arquivo criado em 13/12/2010
//		     Microsoft Visual Studio C++ 2008
//
//				     Thiago Bragante Macedo
//              tmacedo29@gmail.com
//=====================================================

#include <tgaimage.h>
#include <gendef.h>

#define _CRT_SECURE_NO_DEPRECATE // used to avoid warnings about unsafe operations ( e.g.: fopen ) 

TGAImage::TGAImage()
{
  _image = NULL;
}

TGAImage::~TGAImage()
{
  Free();
}

void TGAImage::Free(void)
{
	delete [] _image;
	_image = NULL;
}

void TGAImage::SwitchRedBlue( void )
{
  unsigned char tempBuffer;
	RGBA* source = (RGBA*) _image;
	int pixel;

  switch ( _colorDepth )
  {
    case 24:
    case 32:
      for (pixel = 0; pixel < ( _width * _height ); ++pixel )
      {
        tempBuffer = source[pixel].b;
				source[pixel].b = source[pixel].r;
				source[pixel].r = tempBuffer;
      }
    default:
      /// Other color depths will be ignored
      break;
  }
}

bool TGAImage::ConvertRGBAtoRGB(void)
{
	if ((_colorDepth == 32) && (_imageFormat == IMAGE_RGBA))
	{
		RGB *newImage = new RGB[_width * _height];

		if (!newImage) return false;

		RGB *dest = newImage;
		RGBA *orig = (RGBA*) _image;

		for (int x = 0; x < _width; x++)
		{
			for (int y = 0; y < _height; y++)
			{
				dest->r = orig->r;
				dest->g = orig->g;
				dest->b = orig->b;

				++orig;
				++dest;
			}
		}
		
    delete [] _image;
		_image = (unsigned char*) newImage;
		_colorDepth   = 24;
		_imageType 		= IMAGE_DATA_UNSIGNED_BYTE;
		_imageFormat	= IMAGE_RGB;

		return true;
	}
	return false;
}

bool TGAImage::ConvertRGBtoRGBA( unsigned char alpha )
{
	if ((_colorDepth == 24) && (_imageFormat == IMAGE_RGB))
	{
		RGBA *newImage = new RGBA[_width * _height];

		if (!newImage)
			return false;

		RGBA *dest = newImage;
		RGB  *orig = (RGB*)_image;

		for (int x = 0; x < _width; x++)
		{
			for (int y = 0; y < _height; y++)
			{
				dest->r = orig->r;
				dest->g = orig->g;
				dest->b = orig->b;
				dest->a = alpha;
				++orig;
				++dest;
			}
		}
		delete [] _image;
		_image			  = (unsigned char*)newImage;
		_colorDepth   = 32;
		_imageType		= IMAGE_DATA_UNSIGNED_BYTE;
		_imageFormat	= IMAGE_RGBA;

		return true;
	}
	return false;
}

bool TGAImage::VerticalRotate (void)
{
	if (!_image) return false;

	if (_colorDepth == 32)
	{
		RGBA* tempBits = new RGBA[_height];
		if (!tempBits) return false;

		int lineHeight = _height * 4;

		RGBA* top = (RGBA*)_image;
		RGBA* bottom = (RGBA*)(_image + lineHeight*(_width-1));

		for (int i = 0; i < (_width / 2); ++i)
		{
			memcpy(tempBits, top, lineHeight); 
			memcpy(top, bottom, lineHeight);
			memcpy(bottom, tempBits, lineHeight);

			top = (RGBA*)((unsigned char*)top + lineHeight);
			bottom = (RGBA* )((unsigned char*)bottom - lineHeight);
		}

		delete [] tempBits;
		tempBits = 0;
	}
	else if (_colorDepth == 24)
	{
		RGB* tempBits = new RGB[_height];
		if (!tempBits) return false;

		int lineHeight = _height * 3;

		RGBA* top = (RGBA*)_image;
		RGBA* bottom = (RGBA*)(_image + lineHeight*(_width-1));

		for (int i = 0; i < (_width / 2); ++i)
		{
			memcpy(tempBits, top, lineHeight); 
			memcpy(top, bottom, lineHeight);
			memcpy(bottom, tempBits, lineHeight);

			top = (RGBA*)((unsigned char*)top + lineHeight);
			bottom = (RGBA* )((unsigned char*)bottom - lineHeight);
		}

		delete [] tempBits;
		tempBits = 0;
	}

	return true;
}



bool TGAImage::Load( const string& filename)
{
	FILE *file;
	TGAHeader TGACab;

	file = fopen(filename.c_str(),"rb");
	if (!file) return false;

	// Read the TGA Header 
	fread(&TGACab, 1, sizeof(TGACab), file);

	// Check if image type is valid (RGB, RGB RLE, GRAYSCALE, GRAYSCALE RLE)
	if ( ((TGACab.ImageCodeType != TGA_RGB)     && (TGACab.ImageCodeType != TGA_GRAYSCALE) && 
		    (TGACab.ImageCodeType != TGA_RGB_RLE) && (TGACab.ImageCodeType != TGA_GRAYSCALE_RLE)) ||
		     TGACab.ColorMapType != 0)
	{
		fclose(file);
		return false;
	}

	// Get the Height and Width
	_height = TGACab.Width;
	_width  = TGACab.Height;

	// colorMode -> 3 = BGR, 4 = BGRA
	int colorMode;
		
	colorMode = TGACab.BPP / 8;

	// Do not accept colorMode < 24 bits
	if (colorMode < 3)
	{
		fclose(file);
		return false;
	}

  // Calculates the total size of the image
	_imageSize = _height * _width * colorMode;

	// Aloca memoria para a Imagem TGA
	_image = new unsigned char[_imageSize];

	// Skip the last ID
	if (TGACab.SizeID > 0) fseek( file, SEEK_CUR, TGACab.SizeID);

	// Read the Image
	if (TGACab.ImageCodeType == TGA_RGB || TGACab.ImageCodeType == TGA_GRAYSCALE)
	{
		fread(_image, 1, _imageSize, file);
	}
	else 
	{
		// this is an RLE compressed image
		unsigned char id;
		unsigned char size;
		RGBA color = { 0, 0, 0, 0 };
		unsigned int i = 0;

		while (i < _imageSize)
		{
			id = fgetc(file);

			// See if this is run length data
			if (id >= 128)// & 0x80)
			{
				// Search for the size
				size = (unsigned char)(id - 127);

				// Os proximos 3 ou 4 bytes sao valores repetidos
				color.b = (unsigned char)fgetc(file);
				color.g = (unsigned char)fgetc(file);
				color.r = (unsigned char)fgetc(file);

				if (colorMode == 4)	color.a = (unsigned char)fgetc(file);

				// Save all
				while (size > 0)
				{
					_image[i++] = color.b;
					_image[i++] = color.g;
					_image[i++] = color.r;

					if (colorMode == 4)	_image[i++] = color.a;

					--size;
				}
			}
			else
			{
				// Number of non-RLE pixels
				size = unsigned char(id + 1);

				while (size > 0)
				{
					color.b = (unsigned char)fgetc(file);
					color.g = (unsigned char)fgetc(file);
					color.r = (unsigned char)fgetc(file);

					if (colorMode == 4) color.a = (unsigned char)fgetc(file);

					_image[i++] = color.b;
					_image[i++] = color.g;
					_image[i++] = color.r;

					if (colorMode == 4)	_image[i++] = color.a;

					--size;
				}
			}
		}
	}
	fclose(file);

	switch(TGACab.ImageCodeType)
	{
	  case TGA_RGB:
	  case TGA_RGB_RLE:
		    if (colorMode == 3)
		    {
			    _imageFormat	= IMAGE_RGB;
			    _imageType		= IMAGE_DATA_UNSIGNED_BYTE;
			    _colorDepth   = 24;
		    }
		    else
		    {
			    _imageFormat	= IMAGE_RGBA;
			    _imageType		= IMAGE_DATA_UNSIGNED_BYTE;
			    _colorDepth   = 32;
		    }
		  break;

	  case TGA_GRAYSCALE:
	  case TGA_GRAYSCALE_RLE:
		    _imageFormat	= IMAGE_LUMINANCE;
		    _imageType		= IMAGE_DATA_UNSIGNED_BYTE;
		    _colorDepth   = 8;
		  break;
	}
	if ((TGACab.ImageDescr & TOP_LEFT) == TOP_LEFT) VerticalRotate();

	SwitchRedBlue();

	return (_image != NULL);
}
